﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.CodeDom.Compiler;
using System.Reflection;

namespace GRTVPluginConsole.Webserver.Compiler
{
    class ClassFromHTML
    {
        enum NeedTOFind
        {
            LOWER_THAN, CSHARP_IDENTIFIER, CSHARP_DIRECTIVE, END_OF_CSHARP_IDENTIFIER, GREATER_THAN
        }
        string filename;
        StringBuilder sbToFill;
        StringBuilder sb = new StringBuilder();
        CompilerResults cr;

        public ClassFromHTML(string filename, StringBuilder sbToFill)
        {
            this.filename = filename;
            this.sbToFill = sbToFill;

            sb.Append("using System;\r\n");
            sb.Append("using System.Collections.Generic;\r\n");
            sb.Append("using System.IO;\r\n");
            sb.Append("using System.Text;\r\n");
            
            sb.Append("namespace GRTVPluginConsole.Webserver.Compiler {\n");
            sb.Append("  public  class AUTO {\r\n");
            sb.Append("     string filename;\r\n");
            sb.Append("     StringBuilder sbToFill;\r\n");
            //sb.Append("     byte[] bytes = new byte[1024];\r\n");
            //sb.Append("     int readedBytes = 0;\r\n");
            sb.Append("     public AUTO(string filename, StringBuilder sbToFill) {\r\n");
            sb.Append("         this.filename = filename;\r\n");
            sb.Append("         this.sbToFill = sbToFill;\r\n");
            sb.Append("     }\r\n");
            //sb.Append("     public void readRawData(BinaryReader reader, int fromOffset, int toOffset) {\r\n");

            //sb.Append("     if (readedBytes < fromOffset)\r\n");
            //sb.Append("     {\r\n");
            //sb.Append("         while (readedBytes < fromOffset)\r\n");
            //sb.Append("         {\r\n");
            //sb.Append("             int skip = Math.Min(bytes.Length, fromOffset - readedBytes);\r\n");
            //sb.Append("             int read = reader.Read(bytes, 0, skip);\r\n");
            //sb.Append("             readedBytes += read;\r\n");
            //sb.Append("         }\r\n");
            //sb.Append("     }\r\n");

            //sb.Append("     while (toOffset >= readedBytes)\r\n");
            //sb.Append("     {\r\n");
            //sb.Append("         int skip = Math.Min(bytes.Length, toOffset - readedBytes + 1);\r\n");
            //sb.Append("         int read = reader.Read(bytes, 0, skip);\r\n");
            //sb.Append("         sbToFill.Append(Encoding.ASCII.GetString(bytes, 0, read));\r\n");
            //sb.Append("         readedBytes += read;\r\n");
            //sb.Append("     }\r\n");

            
            //sb.Append("     }\r\n");
            sb.Append("     public  void execute(HttpParameters httpParams) {\r\n");
            sb.Append("        FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);\r\n");
            sb.Append("        BinaryReader reader = new BinaryReader(fs);\r\n");
            //sb.Append("        readedBytes = 0;\r\n");
            sb.Append("        byte[] bytes = new byte[fs.Length];\r\n");
            sb.Append("        int iTotBytes = 0;\r\n");
            sb.Append("        int read;\r\n");
            sb.Append("        while ((read = reader.Read(bytes, iTotBytes, bytes.Length - iTotBytes)) > 0)\r\n");
            sb.Append("        {\r\n");
            //sb.Append("            sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read);\r\n");
            sb.Append("            iTotBytes = iTotBytes + read;\r\n");
            sb.Append("        }\r\n");
            sb.Append("        reader.Close();\r\n");
            sb.Append("        fs.Close();\r\n");

            char[] buffer = new char[1024];
            using (StreamReader sr = new StreamReader(filename))
            {
                int nbRead = 0;
                int startReadOffset = 0;
                int nextStreamIndexToRead = 0;
                char currentDirective = '.';
                NeedTOFind needToFind = NeedTOFind.LOWER_THAN;
                while ((nbRead = sr.ReadBlock(buffer, 0, 1024)) > 0)
                {
                    for (int i = 0; i < nbRead; i++)
                    {
                        nextStreamIndexToRead++;
                        if (needToFind == NeedTOFind.LOWER_THAN)
                        {
                            if (buffer[i] == '<')
                            {
                                needToFind = NeedTOFind.CSHARP_IDENTIFIER;
                            }
                        }
                        else if (needToFind == NeedTOFind.CSHARP_IDENTIFIER)
                        {
                            if (buffer[i] == '#')
                            {
                                needToFind = NeedTOFind.CSHARP_DIRECTIVE;
                                sb.Append("        sbToFill.Append(Encoding.ASCII.GetString(bytes, " + startReadOffset + ", " + (nextStreamIndexToRead - 3 - startReadOffset + 1) + "));\r\n");
//                                sb.Append("        readRawData(reader, " + startReadOffset + ", " + (nextStreamIndexToRead - 3) + ");\r\n");
                            }
                            else if (buffer[i] == '<')
                            {
                                needToFind = NeedTOFind.CSHARP_IDENTIFIER;
                            }
                            else
                            {
                                needToFind = NeedTOFind.LOWER_THAN;
                            }

                        }
                        else if (needToFind == NeedTOFind.CSHARP_DIRECTIVE)
                        {
                            if (buffer[i] == ' ')
                            {
                                currentDirective = buffer[i];
                                sb.Append("        ");
                                needToFind = NeedTOFind.END_OF_CSHARP_IDENTIFIER;
                            }
                            else if (buffer[i] == '=')
                            {
                                currentDirective = buffer[i];
                                sb.Append("        sbToFill.Append(");
                                needToFind = NeedTOFind.END_OF_CSHARP_IDENTIFIER;
                            }
                            else
                            {
                                needToFind = NeedTOFind.LOWER_THAN;
                            }
                        }
                        else if (needToFind == NeedTOFind.END_OF_CSHARP_IDENTIFIER)
                        {
                            if (buffer[i] == '#')
                            {
                                needToFind = NeedTOFind.GREATER_THAN;
                            }
                            else
                            {
                                sb.Append(buffer[i]);
                            }
                        }
                        else if (needToFind == NeedTOFind.GREATER_THAN)
                        {
                            if (buffer[i] == '>')
                            {
                                needToFind = NeedTOFind.LOWER_THAN;
                                if (currentDirective == '=')
                                {
                                    sb.Append(");");
                                }
                                sb.Append("\r\n");
                                startReadOffset = nextStreamIndexToRead;
                            }
                            else
                            {
                                needToFind = NeedTOFind.END_OF_CSHARP_IDENTIFIER;
                                sb.Append("#");
                                sb.Append(buffer[i]);
                            }
                        }
                    }
                }
                if ((nextStreamIndexToRead - 1) >= startReadOffset) {
                    sb.Append("        sbToFill.Append(Encoding.ASCII.GetString(bytes, " + startReadOffset + ", " + (nextStreamIndexToRead - 1 - startReadOffset + 1) + "));\r\n");
                    //sb.Append("        readRawData(reader, " + startReadOffset + ", " + (nextStreamIndexToRead - 1) + ");\r\n");
                }
            }

            //sb.Append("        reader.Close();\r\n");
            //sb.Append("        fs.Close();\r\n");
            sb.Append("     }\r\n");
            sb.Append("  }\r\n");
            sb.Append("}\r\n");


            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateInMemory = true;
            cp.GenerateExecutable = false;
            cp.IncludeDebugInformation = false;
            cp.ReferencedAssemblies.Add("system.dll");
            cp.ReferencedAssemblies.Add("GRTVPluginConsole.exe");
            cp.OutputAssembly = "GRTVPluginConsole";
            

            cr = provider.CompileAssemblyFromSource(cp, sb.ToString());
        }

        public void execute(HttpParameters httpParams)
        {
            sbToFill.Clear();
            if (cr.Errors.HasErrors)
            {
                sbToFill.Append("<html><body><h1>ERROR COMPILATION ON THE FLY</h1>");
                //for (int i = 0; i < cr.Output.Count; i++)
                //{
                //    sbToFill.Append("<br>");
                //    sbToFill.Append(cr.Output[i].ToString());

                //}
                foreach (CompilerError ce in cr.Errors)
                {
                    if (ce.IsWarning) continue;
                    sbToFill.Append("<br>");
                    sbToFill.Append(ce.ToString());
                }
                sbToFill.Append("<hr><pre>");
                sbToFill.Append(sb.ToString().Replace("\r\n", "<br>"));
                sbToFill.Append("</pre></body></html>");
            }
            else
            {
                Type obj = cr.CompiledAssembly.GetType("GRTVPluginConsole.Webserver.Compiler.AUTO");
                ConstructorInfo ci = obj.GetConstructor(new Type[] { typeof(string), typeof(StringBuilder) });
                object instance = ci.Invoke(new object[] { filename, sbToFill });
                MethodInfo method = obj.GetMethod("execute", new Type[] { typeof(HttpParameters) });
                method.Invoke(instance, new object[] {httpParams});
            }
        }
    }
}
